<template>
  <div ref="containerRef" class="container" @scroll="handleScroll">
    <div ref="panelRef" class="panel" :style="{ paddingTop: `${paddingTop}px` }">
      <slot
        v-for="(item, index) in visibleList"
        :key="item[key] || item.actualIndex"
        name="item"
        :element="item"
        :index="index"
        :actual-index="start + index"
      ></slot>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { computed, onMounted, ref, watch } from 'vue'
  import { throttle } from 'lodash'

  const props = defineProps({
    value: {
      type: Array,
      default: () => []
    },

    key: {
      type: String,
      default: ''
    }
  })

  const containerRef = ref(null)
  const panelRef = ref(null)
  let buffTop = 10,
    buffBottom = 10,
    count = 0

  const start = ref(0),
    end = ref(1)
  let itemHeight = 1
  const paddingTop = ref(0)
  const visibleList = computed(() => {
    return props.value?.slice(start.value, end.value) || []
  })

  watch(
    () => props.value?.length,
    (val) => {
      if (val) {
        panelRef.value.style.height = props.value.length * itemHeight + 'px'
      }
    }
  )

  onMounted(() => {
    itemHeight = panelRef.value.firstElementChild?.offsetHeight || 80
    panelRef.value.style.height = props.value?.length * itemHeight + 'px'
    count = Math.floor(containerRef.value?.offsetHeight / itemHeight)
    end.value = count + buffBottom
  })

  function onScroll(e) {
    let startValue = Math.floor(e.target.scrollTop / itemHeight)
    let buff = startValue > buffTop ? buffTop : startValue
    start.value = startValue - buff
    end.value = startValue + count + buffBottom
    paddingTop.value = start.value * itemHeight
  }
  const handleScroll = throttle(onScroll, 100)
</script>

<style lang="scss" scoped>
  .container {
    overflow-y: scroll;
  }
</style>
